package com.partsoft.dits.utils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Types;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;
import org.springframework.util.ReflectionUtils.FieldFilter;
import org.springframework.util.ReflectionUtils.MethodCallback;
import org.springframework.util.ReflectionUtils.MethodFilter;
import org.springframework.util.StringUtils;

import com.partsoft.dits.anns.Concatenation;
import com.partsoft.dits.anns.ConcatenationType;
import com.partsoft.dits.anns.Message;
import com.partsoft.dits.anns.MessageProperty;
import com.partsoft.dits.message.BaseMessageDataItem;
import com.partsoft.dits.message.BaseMessageEntity;
import com.partsoft.dits.message.BaseMessageHeader;
import com.partsoft.dits.message.DataAction;
import com.partsoft.dits.message.DataProperty;
import com.partsoft.dits.message.MessageConcatenationResolver;
import com.partsoft.dits.message.MessageDataConcatenationFilter;
import com.partsoft.dits.message.MessageDataItem;
import com.partsoft.dits.message.MessageDataItemDataActionResolver;
import com.partsoft.dits.message.MessageDataItemPropertyFilter;
import com.partsoft.dits.message.MessageDataItemVisitor;
import com.partsoft.dits.message.MessageEntity;
import com.partsoft.dits.message.MessageEntityBuildCallback;
import com.partsoft.dits.message.MessageEntityVisitor;
import com.partsoft.dits.message.MessageException;
import com.partsoft.dits.message.MessageVisitor;

public abstract class MessageEntityUtils {

	private static void doTraverseMessage(MessageDataItem dataItem, MessageVisitor visitor) throws MessageException {
		Assert.notNull(dataItem);
		Assert.notNull(visitor);
		for (MessageEntity parent : dataItem.getParents()) {
			doTraverseMessage(parent, visitor);
		}
		if (visitor instanceof MessageDataItemVisitor)
			((MessageDataItemVisitor) visitor).visitDataItem(dataItem);
		for (MessageEntity child : dataItem.getChilds()) {
			doTraverseMessage(child, visitor);
		}
	}

	private static void doTraverseMessage(MessageEntity entity, MessageVisitor visitor) throws MessageException {
		Assert.notNull(entity);
		Assert.notNull(visitor);
		if (visitor instanceof MessageEntityVisitor)
			((MessageEntityVisitor) visitor).visitMessageEntity(entity);
		for (MessageDataItem dataItem : entity.getDatas()) {
			doTraverseMessage(dataItem, visitor);
		}
	}

	/**
	 * 遍历消息，所有父级实体的数据对象会被优先遍历。
	 * 
	 * @param entity
	 *            消息实体({@link MessageEntity})
	 * @param visitor
	 *            消息遍历接口({@link MessageVisitor})<br/>
	 *            <ol>
	 *            <li>参数visitor实现{@link MessageEntityVisitor}接口会遍历所有级联消息（包括子自己）</li>
	 *            <li>参数visitor实现{@link MessageDataItemVisitor}
	 *            接口会遍历所有级联消息数据对象（包括子自己）</li>
	 *            </ol>
	 */
	public static void traverseMessage(MessageEntity entity, MessageVisitor visitor) throws MessageException {
		doTraverseMessage(entity, visitor);
	}

	/**
	 * 校验消息数据项是否存在期望的字段列表(允许少于期望的字段)<br/>
	 * 消息至少存在一个数据对象，否则抛错
	 * 
	 * @param entity
	 *            消息实体
	 * @param expectColumns
	 *            期望的字段列表
	 * @throws MessageException
	 *             如果消息数据项的字段为空或有不存在于expectColumns中的字段时则抛出异常
	 */
	public static void validateMessageDataItemExpectColumns(MessageEntity entity, List<DataProperty> expectColumns)
			throws MessageException {
		validateMessageDataItemExpectColumns(entity, expectColumns, true);
	}

	/**
	 * 校验消息数据项是否存在期望的字段列表(允许少于期望的字段)<br/>
	 * 消息至少存在一个数据对象，否则抛错
	 * 
	 * @param entity
	 *            消息实体
	 * @param expectColumns
	 *            期望的字段列表
	 * @throws MessageException
	 *             如果消息数据项的字段为空或有不存在于expectColumns中的字段时则抛出异常
	 */
	public static void validateMessageDataItemExpectColumns(MessageEntity entity, DataProperty[] expectColumns)
			throws MessageException {
		validateMessageDataItemExpectColumns(entity, expectColumns, true);
	}

	/**
	 * 校验消息数据项是否存在期望的字段列表<br/>
	 * 消息至少存在一个数据对象，否则抛错
	 * 
	 * @param entity
	 *            消息实体
	 * @param expectColumns
	 *            期望的字段列表
	 * @param allowLose
	 *            是否允许少于期望的字段
	 * @throws MessageException
	 *             如果消息数据项的字段为空或有不存在于expectColumns中的字段时则抛出异常
	 */
	public static void validateMessageDataItemExpectColumns(MessageEntity entity, List<DataProperty> expectColumns,
			boolean allowLose) throws MessageException {
		Assert.notNull(entity);
		Assert.notEmpty(expectColumns);
		Assert.isTrue(entity.hasData(), "消息至少存在一个数据对象");
		Map<DataProperty, DataProperty> tableColumnsMap = new HashMap<DataProperty, DataProperty>();
		for (DataProperty cln : expectColumns) {
			tableColumnsMap.put(cln, cln);
		}
		for (MessageDataItem dateItem : entity.getDatas()) {
			if (!dateItem.hasValues())
				throw new MessageException("消息格式不正确(缺少列数据)", entity);
			for (DataProperty tCln : dateItem.getValues().keySet()) {
				boolean inCln = tableColumnsMap.containsKey(tCln);
				if (!inCln) {
					throw new MessageException(String.format("消息格式不正确，列(%s)不在限定的列表中。", tCln.getPropertyName()), entity);
				} else {
					tCln.setValueType(tableColumnsMap.get(tCln).getValueType());
					tCln.setValueSize(tableColumnsMap.get(tCln).getValueSize());
				}
			}
		}

	}

	/**
	 * 校验消息数据项是否存在期望的字段列表<br/>
	 * 消息至少存在一个数据对象，否则抛错
	 * 
	 * @param entity
	 *            消息实体
	 * @param expectColumns
	 *            期望的字段列表
	 * @param allowLose
	 *            是否允许少于期望的字段
	 * @throws MessageException
	 *             如果消息数据项的字段为空或有不存在于expectColumns中的字段时则抛出异常
	 */
	public static void validateMessageDataItemExpectColumns(MessageEntity entity, DataProperty[] expectColumns,
			boolean allowLose) throws MessageException {
		Assert.notEmpty(expectColumns);
		validateMessageDataItemExpectColumns(entity, Arrays.asList(expectColumns), allowLose);
	}

	private static Method findGetMethod(Class<?> pojoClazz, String fieldName, Class<?> fieldType) {
		Assert.notNull(pojoClazz);
		Assert.hasText(fieldName);
		Assert.notNull(fieldType);
		String startMethod = fieldType.getName().equals("boolean")
				|| fieldType.getName().equals(Boolean.class.getName()) ? "is" : "get";
		String getMethodName = new StringBuilder(startMethod) + fieldName.substring(0, 1).toUpperCase()
				+ fieldName.substring(1);
		return ClassUtils.getMethodIfAvailable(pojoClazz, getMethodName);
	}

	/**
	 * 添加消息属性值
	 * 
	 * @param msgItem
	 * @param pojo
	 * @param pname
	 * @param fieldValue
	 * @param ptype
	 */
	private static void addMessagePropertyValue(BaseMessageDataItem msgItem, Object pojo, String pname,
			Object fieldValue, int ptype) {
		Assert.notNull(msgItem);
		if (fieldValue != null) {
			Assert.hasText(pname);
			String pvalue = null;
			pvalue = SQLTypeUtils.toStringValue(fieldValue, ptype);
			msgItem.putValue(new DataProperty(pname, ptype), pvalue);
		}
	}

	/**
	 * 添加级联消息
	 * 
	 * @param msgItem
	 * @param pojo
	 * @param pcode
	 * @param fieldValue
	 * @param parent
	 * @throws MessageException
	 */
	private static void addConcatenationMessage(BaseMessageDataItem msgItem, Object pojo, String pcode, String pname,
			String pfk, Object fieldValue, boolean parent, MessageEntityBuildCallback resolve) throws MessageException {
		Assert.notNull(msgItem);
		if (fieldValue != null) {
			if ((fieldValue.getClass().isArray() || fieldValue instanceof Object[] || fieldValue instanceof Collection)
					&& parent) {
				throw new MessageException("父级消息不能为数字或者集合类型。");
			}
			BaseMessageEntity cmsg = new BaseMessageEntity();
			cmsg.setHeader(new BaseMessageHeader(new StringBuilder(pcode).append(CalendarUtils.toID(new Date()))
					.toString(), pcode, msgItem.getOwner().getHeader().getFrom(), msgItem.getAction(), new Date()));
			cmsg.setOwner(msgItem.getOwner());
			convertPojoToMessageEntity(cmsg, null, fieldValue, msgItem.getAction(), msgItem.getOwner().getHeader()
					.getFrom(), resolve);
			if (parent) {
				Assert.hasText(pname, "如果为Parent级联消息时，name属性不能为空");
				Assert.hasText(pfk, "如果为Parent级联消息时，fk属性不能为空");
				String pfkValue = cmsg.getDatas()[0].getValues().get(new DataProperty(pfk));
				Assert.notNull(pfkValue, String.format("外键(%s)关联父消息属性(%s)数据不能为null", pname, pfk));
				msgItem.putValue(pname, pfkValue);
				boolean concatenation = false;
				if (resolve != null && resolve instanceof MessageDataConcatenationFilter) {
					concatenation = ((MessageDataConcatenationFilter) resolve).accept(pojo, msgItem, cmsg,
							new DataProperty(pname), true);
				}
				if (concatenation) {
					msgItem.addParent(cmsg);
				}
			} else {
				boolean concatenation = false;
				if (resolve != null && resolve instanceof MessageDataConcatenationFilter) {
					concatenation = ((MessageDataConcatenationFilter) resolve).accept(pojo, msgItem, cmsg,
							new DataProperty(pname), false);
				}
				if (concatenation) {
					msgItem.addChild(cmsg);
				}
			}
		}
	}

	private static void resolveFieldMessagePropertyValue(BaseMessageDataItem msgItem, Object pojo, Field field,
			Set<String> igMethodNames, MessageEntityBuildCallback resolve) throws MessageException {
		Assert.notNull(msgItem);

		MessageProperty msgp = field.getAnnotation(MessageProperty.class);
		Concatenation msgm = field.getAnnotation(Concatenation.class);
		Assert.isTrue(!(msgp != null && msgm != null),
				String.format("[属性:%s]不能同时注解MessageProperty和Concatenation", field.getName()));

		String pcode = field.getName();
		Object fieldValue = null;

		boolean oldocs = field.isAccessible();
		field.setAccessible(true);
		try {
			fieldValue = field.get(pojo);
		} catch (Throwable e) {
			throw new MessageException(e.getMessage(), e);
		} finally {
			field.setAccessible(oldocs);
		}

		Method gMethod = findGetMethod(pojo.getClass(), field.getName(), field.getType());
		if (gMethod != null) {
			if (msgp == null && msgm == null) {
				msgp = gMethod.getAnnotation(MessageProperty.class);
				msgm = gMethod.getAnnotation(Concatenation.class);
			}
			Assert.isTrue(!(msgp != null && msgm != null),
					String.format("[方法:%s()]不能同时注解MessageProperty和Concatenation", gMethod.getName()));
			gMethod.setAccessible(true);
			try {
				fieldValue = gMethod.invoke(pojo);
			} catch (Throwable e) {
				throw new MessageException(e.getMessage(), e);
			} finally {
				field.setAccessible(oldocs);
			}
			igMethodNames.add(gMethod.getName());
		}

		if (msgm == null) {
			if (fieldValue != null) {
				int ptype = Types.VARCHAR;
				if (msgp != null) {
					pcode = msgp.name().trim().length() > 0 ? msgp.name().trim() : pcode;
					ptype = msgp.type();
					boolean ignored = msgp.ignore();
					if (resolve != null && resolve instanceof MessageDataItemPropertyFilter) {
						ignored = ignored
								|| !(((MessageDataItemPropertyFilter) resolve).accept(fieldValue, msgItem.getOwner(),
										new DataProperty(pcode, ptype)));
					}
					if (ignored)
						return;
				} else {
					ptype = toSqlTypeFromClass(fieldValue.getClass(), ptype);
				}
				addMessagePropertyValue(msgItem, pojo, pcode, fieldValue, ptype);
			}
		} else {
			if (!msgm.ignore()) {
				pcode = msgm.code().trim().length() > 0 ? msgm.code().trim() : pcode;
				String pname = msgm.name().trim();
				String pfk = msgm.fk().trim();
				pfk = pfk.length() > 0 ? pfk : pname;
				addConcatenationMessage(msgItem, pojo, pcode, pname, pfk, fieldValue,
						msgm.type() == ConcatenationType.Parent, resolve);
			}
		}
	}

	protected static void resolveFieldMessagePropertyValue(BaseMessageDataItem msgItem, Object pojo, Method method,
			MessageEntityBuildCallback resolve) throws MessageException {
		Assert.notNull(msgItem);
		MessageProperty msgp = method.getAnnotation(MessageProperty.class);
		Concatenation msgm = method.getAnnotation(Concatenation.class);
		Assert.isTrue(!(msgp != null && msgm != null),
				String.format("[%s]不能同时注解MessageProperty和Concatenation", method.getName()));

		Object fieldValue = null;
		boolean oldacs = method.isAccessible();
		try {
			if (method.getParameterTypes().length > 0)
				return;
			fieldValue = method.invoke(pojo);
		} catch (Throwable e) {
			throw new MessageException(e.getMessage(), e);
		} finally {
			method.setAccessible(oldacs);
		}

		String pcode = method.getName();
		pcode = pcode.startsWith("get") ? pcode.substring("get".length()) : pcode.startsWith("is") ? pcode
				.substring("is".length()) : pcode;
		pcode = pcode.substring(0, 1).toLowerCase() + pcode.substring(1);

		if (msgm == null) {
			int ptype = Types.VARCHAR;
			if (msgp != null) {
				pcode = msgp.name().trim().length() > 0 ? msgp.name().trim() : pcode;
				ptype = msgp.type();
				boolean ignored = msgp.ignore();
				if (resolve != null && resolve instanceof MessageDataItemPropertyFilter) {
					ignored = ignored
							|| !(((MessageDataItemPropertyFilter) resolve).accept(fieldValue, msgItem.getOwner(),
									new DataProperty(pcode, ptype)));
				}
				if (ignored)
					return;
			} else {
				ptype = toSqlTypeFromClass(fieldValue.getClass(), ptype);
			}
			addMessagePropertyValue(msgItem, pojo, pcode, fieldValue, ptype);
		} else {
			if (!msgm.ignore()) {
				pcode = msgm.code().trim().length() > 0 ? msgm.code().trim() : pcode;
				String pname = msgm.name().trim();
				String pfk = msgm.fk().trim();
				pfk = pfk.length() > 0 ? pfk : pname;
				addConcatenationMessage(msgItem, pojo, pcode, pname, pfk, fieldValue,
						msgm.type() == ConcatenationType.Parent, resolve);
			}
		}
	}

	protected static MessageDataItem convertPojoToMessageDataItem(BaseMessageEntity msg, final Object pojo,
			final MessageEntityBuildCallback resolver) throws MessageException {
		final BaseMessageDataItem dataItem = new BaseMessageDataItem(msg);
		if (resolver != null && resolver instanceof MessageDataItemDataActionResolver) {
			DataAction action = ((MessageDataItemDataActionResolver) resolver).resolveDataAction(pojo, msg);
			if (action != null && action != msg.getHeader().getDefaultAction()) {
				dataItem.setAction(action);
			}
		}
		final Set<String> igMethodNames = new LinkedHashSet<String>();
		igMethodNames.add("getClass");
		try {
			ReflectionUtils.doWithFields(ClassUtils.getUserClass(pojo), new FieldCallback() {
				public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
					try {
						resolveFieldMessagePropertyValue(dataItem, pojo, field, igMethodNames, resolver);
					} catch (Throwable e) {
						throw new IllegalArgumentException(e);
					}
				}
			}, new FieldFilter() {
				public boolean matches(Field field) {
					return !Modifier.isStatic(field.getModifiers());
				}
			});
			ReflectionUtils.doWithMethods(pojo.getClass(), new MethodCallback() {
				public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
					try {
						resolveFieldMessagePropertyValue(dataItem, pojo, method, resolver);
					} catch (Throwable e) {
						throw new IllegalArgumentException(e);
					}
				}
			}, new MethodFilter() {
				public boolean matches(Method method) {
					boolean result = false;
					String fname = method.getName();
					if (!Modifier.isStatic(method.getModifiers()) && Modifier.isPublic(method.getModifiers())
							&& !igMethodNames.contains(fname)) {
						result = fname.startsWith("get");
						if (result) {
							fname = fname.substring("get".length());
						} else {
							result = fname.startsWith("is");
							if (result)
								fname.substring("is".length());
							else
								result = false;
						}
						if (result) {
							fname = fname.toLowerCase();
							result = !dataItem.containsKey(fname);
						}
					}
					return result;
				}
			});
		} catch (Throwable e) {
			throw new MessageException(e.getMessage(), e);
		}

		if (resolver != null && resolver instanceof MessageConcatenationResolver) {
			((MessageConcatenationResolver) resolver).resolveConcatenation(dataItem);
		}

		return dataItem;
	}

	@SuppressWarnings("unchecked")
	private static MessageEntity convertPojoToMessageEntity(BaseMessageEntity msg, String messageIdOrVersion,
			Object pojo, DataAction action, String from, MessageEntityBuildCallback actionResolver)
			throws MessageException {
		if (pojo == null)
			throw new MessageException("转成消息结构体的Java对象不能为空");

		LinkedList<Object> objects = new LinkedList<Object>();
		// 判断如果是array
		if (ObjectUtils.isArray(pojo) || pojo instanceof Object[] || pojo instanceof Collection) {
			Collection<Object> vob = (ObjectUtils.isArray(pojo) || pojo instanceof Object[]) ? CollectionUtils
					.arrayToList(pojo) : (Collection<Object>) pojo;
			objects.addAll(vob);
		} else {
			objects.add(pojo);
		}

		if (objects.size() == 0)
			throw new MessageException("转成消息结构体的Java数组或集合至少需要一个元素");

		Class<?> pojoUserClazz = ClassUtils.getUserClass(objects.get(0).getClass());
		action = action == null ? DataAction.GET : action;
		if (msg == null) {
			msg = new BaseMessageEntity();
			Message clazzMsg = AnnotationUtils.findAnnotation(pojoUserClazz, Message.class);
			String pcode = clazzMsg != null ? clazzMsg.code() : pojoUserClazz.getName();
			pcode = pcode.trim().length() > 0 ? pcode : pojoUserClazz.getName();
			messageIdOrVersion = StringUtils.hasText(messageIdOrVersion) ? messageIdOrVersion
					: new StringBuilder(pcode).append(CalendarUtils.toID(new Date())).toString();
			msg.setHeader(new BaseMessageHeader(messageIdOrVersion, pcode, from, action));
		}

		for (Object obj : objects) {
			Assert.isAssignable(pojoUserClazz, obj.getClass(), "转成消息结构体的JAVA数组或者集合必须为相同元素");
			msg.addData(convertPojoToMessageDataItem(msg, obj, actionResolver));
		}
		return msg;
	}

	public static int toSqlTypeFromClass(Class<?> type) {
		return toSqlTypeFromClass(type, Types.VARCHAR);
	}

	public static int toSqlTypeFromClass(Class<?> type, int defaultType) {
		int result = defaultType;
		if (type == null)
			return result;
		if (type.equals(Boolean.class)) {
			if (defaultType != Types.INTEGER && defaultType != Types.BIGINT && defaultType != Types.SMALLINT) {
				result = Types.BOOLEAN;
			}
		} else if (type.equals(Integer.class)) {
			result = Types.INTEGER;
		} else if (type.equals(Double.class)) {
			result = Types.DOUBLE;
		} else if (type.equals(Float.class)) {
			result = Types.FLOAT;
		} else if (type.equals(BigInteger.class)) {
			result = Types.BIGINT;
		} else if (type.equals(BigDecimal.class)) {
			result = Types.DECIMAL;
		} else if (type.equals(Character.class)) {
			result = Types.CHAR;
		} else if (type.equals(String.class)) {
			result = Types.VARCHAR;
		} else if (type.equals(Number.class)) {
			result = Types.NUMERIC;
		} else if (type.equals(Byte.class)) {
			result = Types.BIT;
		} else if (type.getName().equals("boolean")) {
			result = Types.BOOLEAN;
		} else if (type.getName().equals("int")) {
			result = Types.INTEGER;
		} else if (type.getName().equals("double")) {
			result = Types.DOUBLE;
		} else if (type.getName().equals("float")) {
			result = Types.FLOAT;
		} else if (type.getName().equals("long")) {
			result = Types.BIGINT;
		} else if (type.getName().equals("char")) {
			result = Types.CHAR;
		} else if (type.getName().equals("byte")) {
			result = Types.BIT;
		}
		return result;
	}

	/**
	 * 转换POJO至消息实体
	 * 
	 * @param pojo
	 * @return
	 * @throws MessageException
	 */
	public static MessageEntity convertPojoToMessageEntity(Object pojo, DataAction defaultAction)
			throws MessageException {
		return convertPojoToMessageEntity(null, null, pojo, defaultAction, null, null);
	}

	public static MessageEntity convertPojoToMessageEntity(Object pojo, DataAction defaultAction, String from)
			throws MessageException {
		return convertPojoToMessageEntity(null, null, pojo, defaultAction, from, null);
	}

	public static MessageEntity convertPojoToMessageEntity(Object pojo, String messageIdOrVersion,
			DataAction defaultAction, String from) throws MessageException {
		return convertPojoToMessageEntity(null, messageIdOrVersion, pojo, defaultAction, from, null);
	}

	//
	public static MessageEntity convertPojoToMessageEntity(Object pojo, DataAction defaultAction,
			MessageEntityBuildCallback resolver) throws MessageException {
		return convertPojoToMessageEntity(null, null, pojo, defaultAction, null, resolver);
	}

	public static MessageEntity convertPojoToMessageEntity(Object pojo, DataAction defaultAction, String from,
			MessageEntityBuildCallback resolver) throws MessageException {
		return convertPojoToMessageEntity(null, null, pojo, defaultAction, from, resolver);
	}

	public static MessageEntity convertPojoToMessageEntity(Object pojo, String messageIdOrVersion,
			DataAction defaultAction, String from, MessageEntityBuildCallback resolver) throws MessageException {
		return convertPojoToMessageEntity(null, messageIdOrVersion, pojo, defaultAction, from, resolver);
	}

	public static MessageEntity convertToMessageEntity(List<Map<DataProperty, String>> datas, String messageCode,
			String messageIdOrVersion, DataAction action, String from) throws MessageException {
		Assert.notEmpty(datas);
		Assert.hasText(messageCode);
		BaseMessageEntity msg = new BaseMessageEntity();
		messageIdOrVersion = StringUtils.hasText(messageIdOrVersion) ? messageIdOrVersion : new StringBuilder(
				messageCode).append(CalendarUtils.toID(new Date())).toString();
		action = action == null ? DataAction.GET : action;
		msg.setHeader(new BaseMessageHeader(messageIdOrVersion, messageCode, from, action));
		for (Map<DataProperty, String> data : datas) {
			BaseMessageDataItem dItem = new BaseMessageDataItem(msg);
			dItem.setValues(data);
			msg.addData(dItem);
		}
		return msg;
	}

	public static MessageEntity convertToMessageEntity(List<Map<DataProperty, String>> datas, String messageCode,
			DataAction action) throws MessageException {
		return convertToMessageEntity(datas, messageCode, null, action, null);
	}

	public static MessageEntity convertToMessageEntity(List<Map<DataProperty, String>> datas, String messageCode)
			throws MessageException {
		return convertToMessageEntity(datas, messageCode, null, null, null);
	}

}
